package cn.edu.gzgs.config.security;

import cn.edu.gzgs.util.JwtUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

/**
 * JWT 认证过滤器
 *
 * - 从请求头提取并解析 JWT，校验通过则将认证信息写入 SecurityContext
 * - 对公共路径与 CORS 预检请求直接放行
 * - 对空、占位、格式错误或过期的 Token 温和处理并继续放行，避免无意义错误日志
 *
 * @author Zyf
 */
@Component
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtUtils jwtUtils;
    private final UserDetailsService userDetailsService;

    /**
     * 构造函数：注入 JWT 工具与用户详情服务
     *
     * @param jwtUtils JWT 工具类
     * @param userDetailsService 用户详情服务
     */
    public JwtAuthenticationFilter(JwtUtils jwtUtils, UserDetailsService userDetailsService) {
        this.jwtUtils = jwtUtils;
        this.userDetailsService = userDetailsService;
    }

    /** 公共放行路径（无需鉴权） */
    private static final String[] PUBLIC_PATH_PATTERNS = new String[] {
            "/api/auth/login",
            "/api/captcha",
            "/swagger-ui.html",
            "/swagger-ui/**",
            "/v3/api-docs/**",
            "/webjars/**"
    };

    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    /**
     * 决定是否跳过本过滤器。
     *
     * @param request 当前 HTTP 请求
     * @return true 表示跳过过滤
     */
    @Override
    protected boolean shouldNotFilter(@NonNull HttpServletRequest request) {
        String uri = request.getRequestURI();

        // 放行 CORS 预检请求
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            return true;
        }

        // 放行公共路径
        for (String pattern : PUBLIC_PATH_PATTERNS) {
            if (PATH_MATCHER.match(pattern, uri)) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request,
                                    @NonNull HttpServletResponse response,
                                    @NonNull FilterChain filterChain) throws ServletException, IOException {

        // 记录当前请求的URI（调试级别，减少噪音）
        log.debug("JwtAuthenticationFilter: 处理请求，URI: {}", request.getRequestURI());

        // 获取请求头中的授权信息
        final String authHeader = request.getHeader("Authorization");
        final String jwt;
        String username = null; // 初始化用户名变量

        // 检查Authorization头是否存在，且是否以"Bearer "开头
        if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer ")) {
            // 如果没有携带Token，或者格式不正确，直接继续后续过滤链
            log.debug("JwtAuthenticationFilter: Authorization头缺失、不是Bearer类型或为空。URI: {}", request.getRequestURI());
            filterChain.doFilter(request, response);
            return;
        }

        // 提取JWT字符串（去掉"Bearer "部分）
        jwt = authHeader.substring(7);
        log.debug("JwtAuthenticationFilter: Extracted JWT: {}", jwt);

        // 解析前的快速合法性校验，过滤掉空、占位或显著非法的 token
        if (!StringUtils.hasText(jwt) || "null".equalsIgnoreCase(jwt) || "undefined".equalsIgnoreCase(jwt)) {
            log.debug("JwtAuthenticationFilter: JWT 为空或为占位值，直接放行。URI: {}", request.getRequestURI());
            filterChain.doFilter(request, response);
            return;
        }

        int dotCount = StringUtils.countOccurrencesOf(jwt, ".");
        if (dotCount != 2) {
            log.debug("JwtAuthenticationFilter: JWT 格式不正确（应包含2个点），实际: {}，URI: {}", dotCount, request.getRequestURI());
            filterChain.doFilter(request, response);
            return;
        }

        try {
            // 解析JWT，提取用户名
            username = jwtUtils.extractUsername(jwt);
            log.debug("JwtAuthenticationFilter: 从JWT中提取的用户名: {}", username);
        } catch (io.jsonwebtoken.ExpiredJwtException e) {
            // JWT已过期（无需打印堆栈，减少噪音）
            log.debug("JwtAuthenticationFilter: JWT已过期，URI: {}，原因: {}", request.getRequestURI(), e.getMessage());
            filterChain.doFilter(request, response);
            return;
        } catch (Exception e) {
            // 其他解析错误（降级为调试日志，避免误报为错误）
            log.debug("JwtAuthenticationFilter: 解析JWT失败，URI: {}，原因: {}", request.getRequestURI(), e.getMessage());
            filterChain.doFilter(request, response);
            return;
        }

        // 如果用户名存在且当前请求的SecurityContext没有验证信息
        if (StringUtils.hasText(username) && SecurityContextHolder.getContext().getAuthentication() == null) {
            // 输出调试信息，加载用户详情
            log.debug("JwtAuthenticationFilter: 提取到用户名 '{}', 当前未认证，加载UserDetails。", username);
            UserDetails userDetails = null;
            try {
                userDetails = this.userDetailsService.loadUserByUsername(username);
            } catch (org.springframework.security.core.userdetails.UsernameNotFoundException e) {
                // 用户不存在
                filterChain.doFilter(request, response); //  Let Spring Security handle it.
                log.warn("JwtAuthenticationFilter: 用户 '{}' 在数据库中未找到。", username);
                filterChain.doFilter(request, response); // 让Spring Security后续处理
                return;
            }

            // 输出是否含有权限信息
            log.debug("JwtAuthenticationFilter: UserDetails已加载，用户 '{}' 是否有权限: {}", username, !userDetails.getAuthorities().isEmpty());

            // 验证JWT是否合法（是否与用户信息匹配）
            if (jwtUtils.validateToken(jwt, userDetails.getUsername())) {
                // JWT合法，设置认证信息到SecurityContext
                log.info("JwtAuthenticationFilter: JWT对用户 '{}'有效，设置用户名到SecurityContext中。", username);
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails,
                        null,
                        userDetails.getAuthorities()
                );
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            } else {
                // JWT校验失败
                log.debug("JwtAuthenticationFilter: JWT验证失败，用户 '{}'。Token可能无效或用户名不匹配。", username);
            }
        } else {
            // 如果没有用户名或者身份信息已存在
            if (!StringUtils.hasText(username)) {
                log.debug("JwtAuthenticationFilter: JWT中无法提取用户名（为空或null）");
            }
            if (SecurityContextHolder.getContext().getAuthentication() != null) {
                log.debug("JwtAuthenticationFilter: SecurityContext中已存在身份信息，用户名: '{}', 跳过JWT验证。",
                        SecurityContextHolder.getContext().getAuthentication().getName());
            }
        }

        // 继续后续请求链
        filterChain.doFilter(request, response);
    }
} 